float4x4 World;
float4x4 View;
float4x4 Projection;
float2 ViewPortScale;

float size;
float dt;
float3 globalForce;
float attractiveLocalCount;
float3 attractiveLocals[5];
float attractiveLocalMagnitudes[5];
float repulsiveLocalCount;
float3 repulsiveLocals[5];
float repulsiveLocalMagnitudes[5];

float4x4 MatrixTransform;

texture ParticleTex;
sampler PartSampler = sampler_state
{
	Texture = <ParticleTex>;
};

texture PositionTex;
sampler PosSampler = sampler_state
{
    Texture = <PositionTex>;
};

texture VelocityTex;
sampler VelSampler = sampler_state
{
	Texture = <VelocityTex>;
};

struct VertexShaderInput
{
    float2 Corner : TEXCOORD0;
	float Index : POSITION0;
};

struct VertexShaderOutput
{
    float4 Position : POSITION0;
	float2 TextureCoordinate : COLOR1;
};

float ComputeParticleSize(float size)
{   
    // Project the size into screen coordinates.
    return size * Projection._m11;
}

VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{
    VertexShaderOutput output;

	float y = floor(input.Index / 1000);
	float x = input.Index - (y * 1000);
	float4 pos = tex2Dlod(PosSampler, float4(x, y, 0.0, 1.0));

    float4 worldPosition = mul(pos, World);
    float4 viewPosition = mul(worldPosition, View);
    output.Position = mul(viewPosition, Projection);

	output.Position.xy += input.Corner * size * ViewPortScale;
	output.TextureCoordinate = (input.Corner + 1) / 2;

    return output;
}

float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
	return tex2D(PartSampler, input.TextureCoordinate);
}

float4 VelocityPixelShader(float2 texCoord : TEXCOORD0) : COLOR0
{
	float4 velocity = tex2D(VelSampler, texCoord);
	float4 position = tex2D(PosSampler, texCoord);

	float3 totalForce = globalForce;

	float dist;
	for (int i = 0; i < attractiveLocalCount; i++)
	{
		//  
		// F = magnitude / r^2
		//
		dist = distance(position, attractiveLocals[i]);
		totalForce += attractiveLocalMagnitudes[i] / (dist * dist);
	}

	for (int j = 0; j < repulsiveLocalCount; j++)
	{
		//  
		// F =  -magnitude / r^2
		//
		dist = distance(position, repulsiveLocals[j]);
		totalForce -= repulsiveLocalMagnitudes[j] / (dist * dist);
	}

	return float4(velocity.xyz + totalForce * dt, 0);
}

float4 PositionPixelShader(float2 texCoord : TEXCOORD0) : COLOR0
{
	float4 position = tex2D(PosSampler, texCoord);
	float4 velocity = tex2D(VelSampler, texCoord);
	return position + velocity * dt;
}

void SpriteVertexShader(inout float4 color    : COLOR0, 
                        inout float2 texCoord : TEXCOORD0, 
                        inout float4 position : SV_Position) 
{ 
    position = mul(position, MatrixTransform); 
} 

technique Render
{
    pass Pass1
    {
        VertexShader = compile vs_3_0 VertexShaderFunction();
        PixelShader = compile ps_3_0 PixelShaderFunction();
    }
}

technique Velocity
{
	pass Pass1
	{
		VertexShader = compile vs_3_0 SpriteVertexShader();
		PixelShader = compile ps_3_0 VelocityPixelShader();
	}
}

technique Position
{
	pass Pass1
	{
		VertexShader = compile vs_3_0 SpriteVertexShader();
		PixelShader = compile ps_3_0 PositionPixelShader();
	}
}
